package Gr

import (
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"strings"
)

func (cs TableInfoMeta) FuncName(s global.TablesMetaInfoInput) (result global.ObjectNameReturnResult, err error) {
	var f1 any
	var event = "[clusterFuncName]"
	result.BaseResult.Sql = fmt.Sprintf("select ROUTINE_NAME from INFORMATION_SCHEMA.ROUTINES where  ROUTINE_TYPE = 'FUNCTION' AND ROUTINE_SCHEMA ='%v' %v",
		s.TableInfo.Schema, func() string {
			switch {
			case s.TableInfo.Table == "*":
				return ""
			case strings.Contains(s.TableInfo.Table, "%"):
				return fmt.Sprintf(" and ROUTINE_NAME like '%%%v%%'", s.TableInfo.Table)
			default:
				return fmt.Sprintf(" and ROUTINE_NAME = '%v'", s.TableInfo.Table)
			}
		}())
	if result.BaseResult.TimeOut, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.BaseResult.Sql,
		Expect:       ETL.StringGarth,
	}); err != nil || f1 == nil {
		return
	}
	result.Result = f1.([]string)
	return
}

func (cs TableInfoMeta) FuncExist(s global.TablesMetaInfoInput) (result global.ObjectExistReturnResult, err error) {
	var f1 any
	var event = "[clusterFuncExist]"
	result.BaseResult.Sql = fmt.Sprintf("select count(1) as `sum` from INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'FUNCTION' and ROUTINE_SCHEMA = '%v' and ROUTINE_NAME = '%v'", s.TableInfo.Schema, s.TableInfo.Table)
	if result.BaseResult.TimeOut, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.BaseResult.Sql,
		Expect:       ETL.Map,
	}); err != nil || f1 == nil {
		return
	}
	if fmt.Sprintf("%v", f1.(map[string]any)["sum"]) == "1" {
		result.Result = true
	}
	return
}
func FuncDefinitionConvert(x global.FuncResultDefinition) (convert global.FuncDefinitionConvertS) {
	return global.FuncDefinitionConvertS{
		Schema:      x.Schema,
		Name:        x.Name,
		Permissions: x.Permissions,
		Body:        x.Body,
		Comment:     x.Comment,
		PInput:      x.PInput,
		POutput:     x.POutput,
		Type:        x.Type,
		PrintInfo:   x.PrintInfo,
	}
}
func getFuncParameter(s global.SchemaObjectInput) (input, out []global.FuncInputOutParameter, err error) {
	var f1 any
	var event = "[clusterGetFuncParameter]"
	sqlStr := fmt.Sprintf("SELECT \n"+
		"\t\tPARAMETER_NAME AS `pName`,\n    "+
		"\t\tDTD_IDENTIFIER AS `pType`,\n    "+
		"\t\tPARAMETER_MODE AS `pDirection`,\n    "+
		"\t\tORDINAL_POSITION\n"+
		"FROM \n    "+
		"information_schema.PARAMETERS\n"+
		"WHERE 1 = 1 \n    "+
		"\t\tAND SPECIFIC_SCHEMA = '%v'\n    "+
		"\t\tAND SPECIFIC_NAME = '%v' "+
		"\t\torder by "+
		"\t\tORDINAL_POSITION", s.Schema, s.Table)
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: sqlStr,
		Expect:       ETL.SMap,
	}); err != nil || f1 == nil {
		return
	}
	for _, v := range f1.([]map[string]any) {
		var p global.FuncInputOutParameter
		if err = global.MapSubToStructSubJsonName(mapNullActive(v), &p); err != nil {
			return
		}
		switch strings.ToLower(p.PDirection) {
		case "in":
			input = append(input, p)
		case "out":
			out = append(out, p)
		default:
			out = append(out, p)
		}
	}
	return
}
func funcPrintInfo(x global.FuncResultDefinition) (res string) {
	/*
			DELIMITER //

		CREATE FUNCTION  add_and_subtract(
		    IN num1 INT,
		    IN num2 INT,
		    OUT sum_result VARCHAR(255),
		    OUT diff_result VARCHAR(255)
		)
		BEGIN
		    SET sum_result = CAST(num1 + num2 AS CHAR);
		    SET diff_result = CAST(num1 - num2 AS CHAR);
		END //

		DELIMITER ;
	*/
	res = fmt.Sprintf("DELIMITER // \n"+
		"CREATE %v FUNCTION `%v`.`%v`(\n"+
		"%v "+
		"%v"+
		") \n"+
		"%v"+
		"//"+
		"DELIMITER ;",
		x.Permissions,
		x.Schema, x.Name,
		func() string {
			var in []string
			for _, v := range x.PInput {
				in = append(in, fmt.Sprintf("IN %v %v,", v.PName, v.PType))
			}
			return strings.Join(in, "\n")
		}(),
		func() string {
			var out []string
			for _, v := range x.PInput {
				out = append(out, fmt.Sprintf("OUT %v %v,", v.PName, v.PType))
			}
			return strings.Join(out, "\n")
		}(),
		x.Body,
	)
	return
}
func (cs SchemaObjectMeta) Func(s global.SchemaObjectInput) (result global.SchemaObjectReturnResult, err error) {
	var (
		f1    any
		event = "[clusterFunc]"
		x     global.FuncResultDefinition
	)
	result.BaseResult.Sql = fmt.Sprintf("SELECT\n"+
		"\tROUTINE_SCHEMA AS `schema`,\n"+
		"\tROUTINE_NAME AS `name`,\n"+
		"\tDEFINER  AS `permissions`,\n  "+
		"\tROUTINE_DEFINITION AS `body`,\n"+
		"\tROUTINE_COMMENT AS `comment`\n"+
		"FROM\n"+
		"\tinformation_schema.ROUTINES \n"+
		"WHERE 1 = 1\n"+
		"\tAND ROUTINE_TYPE = 'FUNCTION' \n"+
		"\tAND ROUTINE_SCHEMA = '%v' \n"+
		"\tAND ROUTINE_NAME = '%v'", s.Schema, s.Table)
	if result.BaseResult.TimeOut, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.BaseResult.Sql,
		Expect:       ETL.Map,
	}); err != nil || f1 == nil {
		return
	}
	if err = global.MapSubToStructSubJsonName(mapNullActive(f1.(map[string]any)), &x); err != nil {
		return
	}
	x.Type = "func"
	if x.PInput, x.POutput, err = getFuncParameter(s); err != nil {
		return
	}
	x.PrintInfo = funcPrintInfo(x)
	result.FuncResult.Definition = x
	result.FuncResult.Convert = FuncDefinitionConvert(x)
	return
}

func funcPrintInfoConvert(s string) (res string) {
	res = strings.TrimSpace(s)
	if strings.HasSuffix(res, "/") {
		res = strings.TrimSpace(res[:len(res)-1])
	}
	if strings.HasSuffix(res, ";") {
		res = strings.TrimSpace(res[:len(res)-1])
	}
	return
}
func createFunc(s global.SchemaObjectInput) (result global.ObjectFixSqlS, err error) {
	result.CreateSql = append(result.CreateSql, fmt.Sprintf("use %v; \n"+
		"delimiter /\n"+
		"%v \n"+
		"/ \n"+
		"delimiter ;",
		s.Schema,
		funcPrintInfoConvert(s.ObjectDefinition.FuncResult.Convert.PrintInfo)))
	return
}
